package com.ds.infrastructure.hyperspace.container.admin;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.ds.infrastructure.hyperspace.common.admin.AdminResponseVO;
import com.ds.infrastructure.hyperspace.container.admin.processor.Processor;
import com.ds.infrastructure.hyperspace.container.internal.NettyContainer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;

import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

/**
 * @author  Weishaoying
 * @date 2019/6/24 上午10:31
 */
@Slf4j
public class AdminServerHandler extends SimpleChannelInboundHandler {

    private NettyContainer container;

    private HttpRequest httpRequest;

    private ByteBuf requestByteBuf = null;

    private String uri;

    private Map<String, Processor> adminProcessorMap;

    public AdminServerHandler(NettyContainer container, Map<String, Processor> adminProcessorMap) {
        this.adminProcessorMap = adminProcessorMap;
        this.container = container;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof HttpRequest) {
            processHeader((HttpRequest) msg);
        }
        if (msg instanceof HttpContent) {
            processContent(ctx, (HttpContent) msg);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error(cause.getMessage());
        ctx.close();
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    private void processHeader(HttpRequest httpRequest) {
        this.httpRequest = httpRequest;
        requestByteBuf = null;
        HttpHeaders httpHeaders = httpRequest.headers();
        uri = httpRequest.uri();
        Integer contentLength = httpHeaders.getInt(HttpHeaderNames.CONTENT_LENGTH);
        if (contentLength != null) {
            requestByteBuf = Unpooled.buffer(contentLength.intValue());
        } else {
            requestByteBuf = Unpooled.buffer();
        }
    }

    private void processContent(ChannelHandlerContext ctx, HttpContent content) {
        ByteBuf currentContent = content.content();
        if (currentContent.isReadable() && requestByteBuf != null) {
            requestByteBuf.writeBytes(currentContent);
        }
        if (content instanceof LastHttpContent) {
            AdminResponseVO result = new AdminResponseVO();
            try {
                Processor processor = adminProcessorMap.get(uri);
                if (processor == null) {
                    result.setCode(1);
                    result.setMsg("Uri invalid!");
                } else {
                    byte[] msgByteArray = null;
                    if (requestByteBuf != null) {
                        msgByteArray = requestByteBuf.array();
                    }
                    result = processor.process(container, msgByteArray);
                }
            } catch (Exception e) {
                log.error("update config is error.", e);
                result.setCode(1);
                result.setMsg(e.toString());
            } finally {
                try {
                    ReferenceCountUtil.release(requestByteBuf);
                    responseResult(ctx, result);
                } catch (JsonProcessingException e) {
                    log.error("responseResult is error", e);
                }
            }
        }
    }

    private void responseResult(ChannelHandlerContext ctx, AdminResponseVO result) throws JsonProcessingException {
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK,
                Unpooled.wrappedBuffer(NettyContainer.OBJECT_MAPPER.writeValueAsBytes(result)), false);
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
        ctx.write(response);
        if (!HttpUtil.isKeepAlive(httpRequest)) {
            ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
        }
    }
}
